﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Security;
using System.Collections;
using System.Net;
using System.DirectoryServices;
using System.Management;
using log4net;


namespace ConsoleApplicationForTesting
{
    /// <summary>
    /// list of all network computers
    /// www-infos: http://www.codeproject.com/Articles/16113/Retreiving-a-list-of-network-computer-names-using
    /// 1.8.2013 by angaf
    /// </summary>
    //class ListNetworkComputers

    #region NetworkBrowser CLASS
    /// <summary>
    /// Provides a mechanism for supplying a list of all PC names in the local network.
    /// This collection of PC names is used in the form 
    /// 
    /// This class makes use of a DllImport instruction.
    /// The purpose of which is as follows:
    /// When a DllImport declaration is made
    /// in managed code (C#) it is a call to a legacy
    /// unmanaged code module, normally
    /// a C++ Dynamic Link Library. These C++ Dll's are
    /// usually part of the operating system API,
    /// or some other vendors API, and must be 
    /// used to carry out operations that are not
    /// native within the managed code C# framework. 
    /// This is fairly normal within the windows world.
    /// The only thing that needs careful consideration
    /// is the construction of the correct type of STRUCTS,
    /// object pointers, and attribute markers,
    /// which all contribute to making the link
    /// between managed (C#) and unmanaged code (C++) more seamless.
    /// 
    /// This class makes use of the following Dll calls
    /// <list type="bullet">
    /// <item>
    /// <description> Netapi32.dll : NetServerEnum,
    /// The NetServerEnum function lists all servers
    /// of the specified type that are visible in
    /// a domain. For example, an application can call 
    /// NetServerEnum to list all domain controllers
    /// only or all SQL servers only.
    /// You can combine bit masks to list several
    /// types. For example, a value of 0x00000003 
    /// combines the bit masks for SV_TYPE_WORKSTATION
    /// (0x00000001) and SV_TYPE_SERVER (0x00000002).
    /// </description>
    /// </item>
    /// <item>
    /// <description> Netapi32.dll : NetApiBufferFree,
    /// The NetApiBufferFree function frees 
    /// the memory that the NetApiBufferAllocate
    /// function allocates. Call NetApiBufferFree 
    /// to free the memory that other network
    /// management functions return.</description>
    /// </item>
    /// </list>
    /// </summary>
    public sealed class NetworkBrowser
    {
        ILog log = LogManager.GetLogger(typeof(NetworkBrowser));
        
        #region Dll Imports

        //declare the Netapi32 : NetServerEnum method import
        [DllImport("Netapi32", CharSet = CharSet.Auto,SetLastError = true),
        SuppressUnmanagedCodeSecurityAttribute]

        /// <summary>
        /// Netapi32.dll : The NetServerEnum function lists all servers of the specified type that are
        /// visible in a domain. For example, an application can call NetServerEnum
        /// to list all domain controllers only or all SQL servers only.
        /// You can combine bit masks to list several types. 
        /// For example, a value of 0x00000003  combines the bit
        /// masks for SV_TYPE_WORKSTATION (0x00000001) and SV_TYPE_SERVER (0x00000002)
        /// 
        /// domain [in, optional]
        ///     A pointer to a constant string that specifies the name of the domain for which a list of servers is to be returned. The domain name must be a NetBIOS domain name (for example, microsoft). The NetServerEnum function does not support DNS-style names (for example, microsoft.com).
        ///     If this parameter is NULL, the primary domain is implied.
        /// </summary>
        public static extern int NetServerEnum(
            string ServerNane, // must be null
            int dwLevel,
            ref IntPtr pBuf,
            int dwPrefMaxLen,
            out int dwEntriesRead,
            out int dwTotalEntries,
            int dwServerType,
            string domain, // null for login domain
            out int dwResumeHandle
            );

        //declare the Netapi32 : NetApiBufferFree method import
        [DllImport("Netapi32", SetLastError = true),SuppressUnmanagedCodeSecurityAttribute]

        /// <summary>
        /// Netapi32.dll : The NetApiBufferFree function frees 
        /// the memory that the NetApiBufferAllocate function allocates. 
        /// Call NetApiBufferFree to free the memory that other network management functions return.
        /// </summary>
        public static extern int NetApiBufferFree(IntPtr pBuf);

        //create a _SERVER_INFO_100 STRUCTURE
        [StructLayout(LayoutKind.Sequential)]
        public struct _SERVER_INFO_100
        {
            internal int sv100_platform_id;
            [MarshalAs(UnmanagedType.LPWStr)]
            internal string sv100_name;
        }
        #endregion
        #region Public Constructor
        /// <SUMMARY>
        /// Constructor, simply creates a new NetworkBrowser object
        /// </SUMMARY>
        public NetworkBrowser()
        {
            log.Debug("Constructor:NetworkBrowser");
        }
        #endregion
        #region Public Methods

        public IPAddress getIPAddress(string hostname)
        {
            log.Debug("getIPAddress(): hostname=" + hostname);
            IPAddress[] addresslist = Dns.GetHostAddresses(hostname);
            IPAddress answer=null;
            foreach (IPAddress oneIPAddress in addresslist)
            {
                log.Debug("getIPAddress():oneIPAddress=" + oneIPAddress.ToString() + ", Adressfamily:" + oneIPAddress.AddressFamily.ToString());
                if (oneIPAddress.AddressFamily.ToString() == "InterNetwork")
                {
                    answer = oneIPAddress;
                }
            }
            log.Debug("getIPAddress():returns:" + answer);
            return answer;
        }

        /// <summary>
        /// List all Network computernames in the WinNT-Directory
        /// siehe: http://stackoverflow.com/questions/10236249/getting-computer-names-from-my-network-places
        /// </summary>
        /// <returns>a list with all names</returns>
        public List<String> ListNetworkComputersAsString()
        {
            log.Debug("ListNetworkComputersAsString(): entry");
            List<String> _ComputerNames = new List<String>();
            String _ComputerSchema = "Computer";//searching only for computer!
            System.DirectoryServices.DirectoryEntry _WinNTDirectoryEntries = new System.DirectoryServices.DirectoryEntry("WinNT:");
            foreach (System.DirectoryServices.DirectoryEntry _AvailDomains in _WinNTDirectoryEntries.Children)
            {
                log.Debug("ListNetworkComputersAsString():found DirectoryEntryName:" + _AvailDomains.Name);                
                foreach (System.DirectoryServices.DirectoryEntry _PCNameEntry in _AvailDomains.Children)
                {
                    log.Debug("ListNetworkComputersAsString():found SchemaClassName:" + _PCNameEntry.SchemaClassName);                    
                    if (_PCNameEntry.SchemaClassName.ToLower().Contains(_ComputerSchema.ToLower()))
                    {
                        _ComputerNames.Add(_PCNameEntry.Name);
                    }                    
                }
            }
            log.Debug("ListNetworkComputersAsString(): end");
            return _ComputerNames;
        }

        /// <summary>
        /// list of all shares for given machine
        /// Attention: needed wmi-support on machine!
        /// </summary>
        /// <param name="machineName">computer name</param>
        /// <returns>user-list</returns>
        public List<String> getShares(string machineName)
        {
            log.Debug("getShares(): entry machineName=" + machineName);
            List<String> sharesList = new List<String>();            
            try
            {
                var path = string.Format(@"\\{0}\root\cimv2", machineName);
                var query = "select * from win32_share";
                var worker = new ManagementObjectSearcher(path, query);
                var shares = worker.Get();
                foreach (ManagementObject share in shares)
                {
                    log.Debug("found share:" + share["Name"].ToString());
                    sharesList.Add(share["Name"].ToString());
                }
            }
            catch (Exception ex)
            {
                System.Console.WriteLine("  " + ex.Message);
                System.Console.WriteLine("  " + ex.InnerException);
                System.Console.WriteLine("  " + ex.Source);
                log.Error(ex);
            }
            log.Debug("getShares(): exit: sharesList.count=" + sharesList.Count);
            return sharesList;
        }

        /// <summary>
        /// returns all users from a pc
        /// </summary>
        /// <param name="onePCName">name of pc</param>
        /// <returns>list of all users</returns>
        public List<String> getAllUsers(string onePCName)
        {
            log.Debug("getAllUsers(): entry onePCName=" + onePCName);
            var path = string.Format("WinNT://{0},computer", onePCName);
            List<String> userList = new List<String>();
            try
            {
                using (var computerEntry = new DirectoryEntry(path))
                {
                    foreach (DirectoryEntry childEntry in computerEntry.Children)
                    {
                        if (childEntry.SchemaClassName == "User")
                            userList.Add(childEntry.Name);
                        //too mutch output: log.Debug(childEntry.Name);
                    }
                }
            }
            catch (Exception ex)
            {
                System.Console.WriteLine("  " + ex.Message);
                System.Console.WriteLine("  " + ex.InnerException);
                System.Console.WriteLine("  " + ex.Source);
                log.Error(ex);
            }
            log.Debug("getShares(): exit: userList.count=" + userList.Count);
            return userList;
        }

        /// <summary>
        /// returns the local computername
        /// </summary>
        /// <returns>computer name by using class dns</returns>
        public string getLocalComputername()
        {
            log.Debug("getLocalComputername(): entry");
            var hostname = Dns.GetHostName();
            log.Debug("getLocalComputername(): exit hostname=" + hostname);
            return hostname;
        }

        /// <summary>
        /// returns the ip from the local pc
        /// Attention: replaced by function: this.getIPAddress()
        /// </summary>
        /// <returns>ipV4-object, if not set then reurns null</returns>
        public IPAddress disabled_getLocalIP()
        {
            /*
            IPHostEntry host;
            IPAddress localIP = null;            

            host = Dns.GetHostEntry(Dns.GetHostName());
            
            foreach (IPAddress ip in host.AddressList)
            {
                //System.Console.WriteLine("IP:" + ip.ToString() + ", AddressFamily = " + ip.AddressFamily.ToString());
                if (ip.AddressFamily.ToString() == "InterNetwork")
                {
                    localIP = ip;
                    //break;
                }
            }
             
            return localIP;*/
            return null;
        }

        /// <summary>
        /// retunrs the hostname and the domainname
        /// added by angaf
        /// </summary>
        /// <returns>hostname.domainname</returns>
        public string GetDomainNameAndHostname()
        {
            string domainName = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName;
            string hostName = Dns.GetHostName();
            
            string fqdn = "";
            if (!hostName.Contains(domainName))
                fqdn = hostName + "." + domainName;
            else
                fqdn = hostName;
            return fqdn;
        }

        /// <summary>
        /// Uses the DllImport : NetServerEnum with all its required parameters
        /// (see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/netmgmt/netmgmt/netserverenum.asp
        /// or http://msdn.microsoft.com/en-us/library/windows/desktop/aa370623(v=vs.85).aspx
        /// for full details or method signature) to
        /// retrieve a list of domain SV_TYPE_WORKSTATION and SV_TYPE_SERVER PC's
        /// </summary>
        /// <returns>Arraylist that represents
        /// all the SV_TYPE_WORKSTATION and SV_TYPE_SERVER
        /// PC's in the Domain</returns>
        /// <param name="serverType">
        /// added by angaf: type of server for searching in the network.
        /// SV_TYPE_NT = 1000 ::take any workstation or server.
        /// SV_TYPE_WORKSTATION = 1
        /// SV_TYPE_SERVER = 2
        /// for more types see: http://msdn.microsoft.com/en-us/library/windows/desktop/aa370623(v=vs.85).aspx
        /// </param>
        /// Angaf: disabled. da abgelöst durch this.ListNetworkComputersAsString() !!!!!!!!!!!!!!!!
        public ArrayList disabled_getNetworkComputers(int serverType)
        {
            //local fields
            ArrayList networkComputers = new ArrayList();
            const int MAX_PREFERRED_LENGTH = -1;
            IntPtr buffer = IntPtr.Zero;
            IntPtr tmpBuffer = IntPtr.Zero;
            int entriesRead = 0;
            int totalEntries = 0;
            int resHandle = 0;
            int sizeofINFO = Marshal.SizeOf(typeof(_SERVER_INFO_100));
            
            try
            {
                //call the DllImport : NetServerEnum with all its required parameters
                //see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/netmgmt/netmgmt/netserverenum.asp
                //for full details of method signature see here:
                // http://msdn.microsoft.com/en-us/library/windows/desktop/aa370623(v=vs.85).aspx
                int ret = NetServerEnum(
                    null, 
                    100, 
                    ref buffer,
                    MAX_PREFERRED_LENGTH,
                    out entriesRead,
                    out totalEntries,
                    serverType,
                    null, //domain: null for login domain
                    out resHandle);
                //if the returned with a NERR_Success (C++ term), =0 for C#
                if (ret == 0)
                {
                    //loop through all SV_TYPE_WORKSTATION and SV_TYPE_SERVER PC's
                    for (int i = 0; i < totalEntries; i++)
                    {
                        //get pointer to, Pointer to the buffer that received the data from the call to NetServerEnum. 
                        //Must ensure to use correct size of STRUCTURE to ensure correct location in memory is pointed to
                        tmpBuffer = new IntPtr((int)buffer + (i * sizeofINFO));
                        //Have now got a pointer to the list of SV_TYPE_WORKSTATION and 
                        //SV_TYPE_SERVER PC's, which is unmanaged memory
                        //Needs to Marshal data from an unmanaged block of memory to a 
                        //managed object, again using STRUCTURE to ensure the correct data is marshalled 
                        _SERVER_INFO_100 svrInfo = (_SERVER_INFO_100)Marshal.PtrToStructure(tmpBuffer,typeof(_SERVER_INFO_100));

                        //add the PC names to the ArrayList
                        networkComputers.Add(svrInfo.sv100_name);
                    }
                }
            }
            catch (Exception ex)
            {
                System.Console.WriteLine("  " + "Problem with acessing " +
                    "network computers in NetworkBrowser " +
                    "\r\n\r\n\r\n" + ex.Message);
                return null;
            }
            finally
            {
                //The NetApiBufferFree function frees the memory that the NetApiBufferAllocate function allocates
                NetApiBufferFree(buffer);
            }
            //return entries found
            return networkComputers;

        }
        #endregion
    }
    #endregion
}
